#ifndef GAMEKIT_DELEGATE_H
#define GAMEKIT_DELEGATE_H

#include <stdlib.h>
#include <memory>
#include <vector>
#include <iterator>

#include "../gamekit.h"

namespace gamekit {

/*------------------------------------------------------------------------------------*/
/* gamekit::BaseDelegate<R, A> */
/*------------------------------------------------------------------------------------*/

template<typename R, typename A>
class BaseDelegate
{
public:
	R virtual Invoke(const A& args) = 0;
};

/*------------------------------------------------------------------------------------*/
/* gamekit::BaseDelegate<void, A> */
/*------------------------------------------------------------------------------------*/

template<typename A>
class BaseDelegate<void, A>
{
public:
	void virtual Invoke(const A& args) = 0;
};

/*------------------------------------------------------------------------------------*/
/* gamekit::BaseDelegate<R, void> */
/*------------------------------------------------------------------------------------*/

template<typename R>
class BaseDelegate<R, void>
{
public:
	R virtual Invoke() = 0;
};

/*------------------------------------------------------------------------------------*/
/* gamekit::BaseDelegate<void, void> */
/*------------------------------------------------------------------------------------*/

template<>
class BaseDelegate<void, void>
{
public:
	void virtual Invoke() = 0;
};

/*------------------------------------------------------------------------------------*/
/* gamekit::Delegate<R, A> */
/*------------------------------------------------------------------------------------*/

template<typename R, typename A>
class GK_API Delegate
{
	typedef BaseDelegate<R, A> DelegateType;
	
private:
	DelegateType* m_delegate;

public:
	R operator()(const A& args);
	R Invoke(const A& args);

	static Delegate<R, A> From(R (*pfn)(const A&));
	
	template<typename T>
	static Delegate<R, A> From(R (T::*pfn)(const A&), T* t);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::Delegate<void, A> */
/*------------------------------------------------------------------------------------*/

template<typename A>
class GK_API Delegate<void, A>
{
	typedef BaseDelegate<void, A> DelegateType;
	typedef std::vector<DelegateType*> DelegateVector;
	typedef std::vector<DelegateType*> DelegateIterator;

	EXPORT_STL_VECTOR(GK_API, DelegateType*)

private:
	std::vector<DelegateType*> m_delegates;

public:
	void operator()(const A& args);
	void Invoke(const A& args);

	void add(const Delegate<void, A>& delegate);
	void add(void (*pfn)(const A&));
	template<typename T> void add(void (T::*pfn)(const A&), T* t);

	static Delegate<void, A> From(void (*pfn)(const A&));
	
	template<typename T>
	static Delegate<void, A> From(void (T::*pfn)(const A&), T* t);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::Delegate<R, void> */
/*------------------------------------------------------------------------------------*/

template<typename R>
class GK_API Delegate<R, void>
{
	typedef BaseDelegate<R, void> DelegateType;

private:
	DelegateType* m_delegate;

public:
	R operator()();
	R Invoke();

	static Delegate<R, void> From(R (*pfn)());
	
	template<typename T>
	static Delegate<R, void> From(R (T::*pfn)(), T* t);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::Delegate<void, void> */
/*------------------------------------------------------------------------------------*/

template<>
class GK_API Delegate<void, void>
{
	typedef BaseDelegate<void, void> DelegateType;
	typedef std::vector<DelegateType*> DelegateVector;
	typedef std::vector<DelegateType*>::const_iterator DelegateIterator;

	EXPORT_STL_VECTOR(GK_API, DelegateType*)

private:
	DelegateVector m_delegates;

public:
	void operator()(void);
	void Invoke(void);

	void add(const Delegate<void, void>& delegate);
	void add(void (*pfn)(void));
	template<typename T> void add(void (T::*pfn)(void), T* t);

	static Delegate<void, void> From(void (*pfn)());
	template<typename T> static Delegate<void, void> From(void (T::*pfn)(), T* t);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::NonTypeDelegate<R, A> */
/*------------------------------------------------------------------------------------*/

template<typename R, typename A>
class NonTypeDelegate : public BaseDelegate<R, A>
{
private:
	R (*m_pfn)(const A&);
public:
	NonTypeDelegate(R (*pfn)(const A&));

	R Invoke(const A& args);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::NonTypeDelegate<void, A> */
/*------------------------------------------------------------------------------------*/

template<typename A>
class NonTypeDelegate<void, A> : public BaseDelegate<void, A>
{
private:
	void (*m_pfn)(const A&);
public:
	NonTypeDelegate(void (*pfn)(const A&));

	void Invoke(const A& args);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::NonTypeDelegate<R, void> */
/*------------------------------------------------------------------------------------*/

template<typename R>
class NonTypeDelegate<R, void> : public BaseDelegate<R, void>
{
private:
	R (*m_pfn)(void);
public:
	NonTypeDelegate(R (*pfn)(void));

	R Invoke(void);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::NonTypeDelegate<void, void> */
/*------------------------------------------------------------------------------------*/

template<>
class NonTypeDelegate<void, void> : public BaseDelegate<void, void>
{
private:
	void (*m_pfn)(void);
public:
	NonTypeDelegate(void (*pfn)(void));

	void Invoke(void);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::TypeDelegate<T, R, A> */
/*------------------------------------------------------------------------------------*/

template<typename T, typename R, typename A>
class TypeDelegate : public BaseDelegate<R, A>
{
private:
	T* m_t;
	R (T::*m_pfn)(const A&);
public:
	TypeDelegate(T* t, R (T::*pfn)(const A&));

	R Invoke(const A& args);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::TypeDelegate<T, void, A> */
/*------------------------------------------------------------------------------------*/

template<typename T, typename A>
class TypeDelegate<T, void, A> : public BaseDelegate<void, A>
{
private:
	T* m_t;
	void (T::*m_pfn)(const A&);
public:
	TypeDelegate(T* t, void (T::*pfn)(const A&));

	void Invoke(const A& args);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::TypeDelegate<T, R, void> */
/*------------------------------------------------------------------------------------*/

template<typename T, typename R>
class TypeDelegate<T, R, void> : public BaseDelegate<R, void>
{
private:
	T* m_t;
	R (T::*m_pfn)(void);
public:
	TypeDelegate(T* t, R (T::*pfn)(void));

	R Invoke(void);
};

/*------------------------------------------------------------------------------------*/
/* gamekit::TypeDelegate<T, void, void> */
/*------------------------------------------------------------------------------------*/

template<typename T>
class TypeDelegate<T, void, void> : public BaseDelegate<void, void>
{
private:
	T* m_t;
	void (T::*m_pfn)(void);
public:
	TypeDelegate(T* t, void (T::*pfn)(void));

	void Invoke(void);
};

/*====================================================================================*/
/* gamekit::Delegate<R, A> Implementation */
/*====================================================================================*/

template<typename R, typename A>
R Delegate<R, A>::operator()(const A& args)
{
	return Invoke(args);
}

template<typename R, typename A>
R Delegate<R, A>::Invoke(const A& args)
{
	return m_delegate->Invoke(args);
}

template<typename R, typename A>
Delegate<R, A> Delegate<R, A>::From(R (*pfn)(const A&))
{
	Delegate<R, A> delegate;
	delegate.m_delegate = new NonTypeDelegate<R, A>(pfn);

	return delegate;
}

template<typename R, typename A>
template<typename T>
Delegate<R, A> Delegate<R, A>::From(R (T::*pfn)(const A&), T* t)
{
	Delegate<R, A> delegate;
	delegate.m_delegate = new TypeDelegate<T, R, A>(t, pfn);
	return delegate;
}

/*====================================================================================*/
/* gamekit::Delegate<void, A> Implementation */
/*====================================================================================*/

template<typename A>
void inline Delegate<void, A>::operator()(const A& args)
{
	Invoke(args);
}

template<typename A>
void inline Delegate<void, A>::Invoke(const A& args)
{
	typename std::vector<DelegateType*>::const_iterator it;

	for(it = m_delegates.begin(); it != m_delegates.end(); it++)
	{
		DelegateType* delegate = *it;
		delegate->Invoke(args);
	}
}

template<typename A>
void inline Delegate<void, A>::add(const Delegate<void, A>& delegate)
{
	typename std::vector<DelegateType*>::const_iterator it;

	for(it = delegate.m_delegates.begin(); it != delegate.m_delegates.end(); it++)
	{
		m_delegates.push_back(*it);
	}
}

template<typename A>
void inline Delegate<void, A>::add(void (*pfn)(const A& args))
{
	NonTypeDelegate<void, A> *d = new NonTypeDelegate<void, A>(pfn);
	m_delegates.push_back(d);
}

template<typename A>
template<typename T>
void inline Delegate<void, A>::add(void (T::*pfn)(const A& args), T* t)
{
	TypeDelegate<T, void, A> *d = new TypeDelegate<T, void, A>(t, pfn);
	m_delegates.push_back(d);
}

template<typename A>
Delegate<void, A> inline Delegate<void, A>::From(void (*pfn)(const A&))
{
	Delegate<void, A> d;
	d.add(pfn);

	return d;
}

template<typename A>
template<typename T>
Delegate<void, A> inline Delegate<void, A>::From(void (T::*pfn)(const A& args), T* t)
{
	Delegate<void, A> d;
	d.add(pfn, t);

	return d;
}


/*====================================================================================*/
/* gamekit::Delegate<R, void> Implementation */
/*====================================================================================*/

template<typename R>
R inline Delegate<R, void>::operator()()
{
	return Invoke();
}

template<typename R>
R inline Delegate<R, void>::Invoke()
{
	return m_delegate->Invoke();
}

template<typename R>
Delegate<R, void> inline Delegate<R, void>::From(R (*pfn)(void))
{
	Delegate<R, void> delegate;
	delegate.m_delegate = new NonTypeDelegate<R, void>(pfn);

	return delegate;
}

template<typename R>
template<typename T>
Delegate<R, void> inline Delegate<R, void>::From(R (T::*pfn)(void), T* t)
{
	Delegate<R, void> delegate;
	delegate.m_delegate = new TypeDelegate<T, R, void>(t, pfn);
	return delegate;
}

/*====================================================================================*/
/* gamekit::Delegate<void, void> Implementation */
/*====================================================================================*/

void inline Delegate<void, void>::operator()()
{
	Invoke();
}

void inline Delegate<void, void>::Invoke()
{
	for(std::vector<DelegateType*>::const_iterator it = m_delegates.begin(); it != m_delegates.end(); it++)
	{
		DelegateType* delegate = *it;
		delegate->Invoke();
	}
}

void inline Delegate<void, void>::add(const Delegate<void, void>& delegate)
{
	for(std::vector<DelegateType*>::const_iterator it = delegate.m_delegates.begin(); it != delegate.m_delegates.end(); it++)
	{
		m_delegates.push_back(*it);
	}
}

void inline Delegate<void, void>::add(void (*pfn)(void))
{
	NonTypeDelegate<void, void> *d = new NonTypeDelegate<void, void>(pfn);
	m_delegates.push_back(d);
}

template<typename T>
void inline Delegate<void, void>::add(void (T::*pfn)(void), T* t)
{
	TypeDelegate<T, void, void> *d = new TypeDelegate<T, void, void>(t, pfn);
	m_delegates.push_back(d);
}

/*====================================================================================*/
/* gamekit::NonTypeDelegate<R, A> Implementation */
/*====================================================================================*/

template<typename R, typename A>
NonTypeDelegate<R, A>::NonTypeDelegate(R (*pfn)(const A&))
	: m_pfn(pfn)
{

}

template<typename R, typename A>
R NonTypeDelegate<R, A>::Invoke(const A& args)
{
	return m_pfn(args);
}

/*====================================================================================*/
/* gamekit::NonTypeDelegate<void, A> Implementation */
/*====================================================================================*/

template<typename A>
inline NonTypeDelegate<void, A>::NonTypeDelegate(void (*pfn)(const A&))
	: m_pfn(pfn)
{

}

template<typename A>
inline void NonTypeDelegate<void, A>::Invoke(const A& args)
{
	m_pfn(args);
}

/*====================================================================================*/
/* gamekit::NonTypeDelegate<R, void> Implementation */
/*====================================================================================*/

template<typename R>
inline NonTypeDelegate<R, void>::NonTypeDelegate(R (*pfn)(void))
	: m_pfn(pfn)
{

}

template<typename R>
inline R NonTypeDelegate<R, void>::Invoke(void)
{
	return m_pfn();
}

/*====================================================================================*/
/* gamekit::NonTypeDelegate<void, void> Implementation */
/*====================================================================================*/

inline NonTypeDelegate<void, void>::NonTypeDelegate(void (*pfn)(void))
	: m_pfn(pfn)
{

}

inline void NonTypeDelegate<void, void>::Invoke(void)
{
	m_pfn();
}


/*====================================================================================*/
/* gamekit::TypeDelegate<T, R, A> Implementation */
/*====================================================================================*/

template<typename T, typename R, typename A>
TypeDelegate<T, R, A>::TypeDelegate(T* t, R (T::*pfn)(const A&))
	: m_t(t), m_pfn(pfn)
{
}

template<typename T, typename R, typename A>
R TypeDelegate<T, R, A>::Invoke(const A& args)
{
	return (m_t->*m_pfn)(args);
}

/*====================================================================================*/
/* gamekit::TypeDelegate<T, void, A> Implementation */
/*====================================================================================*/

template<typename T, typename A>
inline TypeDelegate<T, void, A>::TypeDelegate(T* t, void (T::*pfn)(const A&))
	: m_t(t), m_pfn(pfn)
{
}

template<typename T, typename A>
inline void TypeDelegate<T, void, A>::Invoke(const A& args)
{
	(m_t->*m_pfn)(args);
}

/*====================================================================================*/
/* gamekit::TypeDelegate<T, R, void> Implementation */
/*====================================================================================*/

template<typename T, typename R>
inline TypeDelegate<T, R, void>::TypeDelegate(T* t, R (T::*pfn)(void))
	: m_t(t), m_pfn(pfn)
{
}

template<typename T, typename R>
inline R TypeDelegate<T, R, void>::Invoke(void)
{
	return (m_t->*m_pfn)();
}

/*====================================================================================*/
/* gamekit::TypeDelegate<T, void, void> Implementation */
/*====================================================================================*/

template<typename T>
inline TypeDelegate<T, void, void>::TypeDelegate(T* t, void (T::*pfn)(void))
	: m_t(t), m_pfn(pfn)
{
}

template<typename T>
inline void TypeDelegate<T, void, void>::Invoke(void)
{
	(m_t->*m_pfn)();
}

}

#endif
